package cn.zzp.user.service.impl;

import cn.zzp.basic.constant.VerifyCodeConstants;
import cn.zzp.basic.exception.BusinessException;
import cn.zzp.basic.jwt.JwtUtils;
import cn.zzp.basic.jwt.LoginData;
import cn.zzp.basic.jwt.RsaUtils;
import cn.zzp.basic.util.HttpUtil;
import cn.zzp.basic.util.JsonResult;
import cn.zzp.basic.util.Md5Utils;
import cn.zzp.basic.util.StrUtils;
import cn.zzp.org.mapper.EmployeeMapper;
import cn.zzp.system.domain.Menu;
import cn.zzp.user.constant.WxConstants;
import cn.zzp.user.domain.LoginInfo;
import cn.zzp.user.domain.User;
import cn.zzp.user.domain.WxUser;
import cn.zzp.user.dto.LoginDto;
import cn.zzp.user.dto.WxBinderDto;
import cn.zzp.user.mapper.LoginInfoMapper;
import cn.zzp.user.mapper.UserMapper;
import cn.zzp.user.mapper.WxUserMapper;
import cn.zzp.user.service.LoginService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Administrator
 * @description:
 * @date 2022/7/30 12:47
 */
@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMapper wxUserMapper;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 管理员登录
     * @param loginDto
     * @return
     */
    @Override
    public JsonResult adminLogin(LoginDto loginDto) {

        // 1. 空值校验
        String account = loginDto.getAccount();
        String checkPass = loginDto.getCheckPass();
        Integer type = loginDto.getType();
        if (StringUtils.isEmpty(account)
        || StringUtils.isEmpty(checkPass)
        || StringUtils.isEmpty(type)) {
            throw new BusinessException("参数不能为空，请重新确认输入");
        }

        // 2. 根据参数查出对应数据
        LoginInfo loginInfo = loginInfoMapper.queryByDto(loginDto);
        // 对数据进行判断
        if (loginInfo == null){
            throw new BusinessException("用户名或者密码错误！");
        }

        // 3. 校验密码
        String md5Password = Md5Utils.encrypByMd5(loginInfo.getSalt() + loginDto.getCheckPass());
        if (!md5Password.equals(loginInfo.getPassword())){
            throw new BusinessException("用户名或密码错误");
        }

        // 4. 校验可用状态
        if (!loginInfo.getDisable()){
            throw new BusinessException("用户被禁用，请联系管理员");
        }


       /* // 4. 生成token，存入redis
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);

        // 5. 将token与loginInfo对象返回前端
        Map<String, Object> map = new HashMap<>();

        // 清空敏感数据再封装，保证数据安全
        map.put("token", token);
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        map.put("loginInfo", loginInfo);*/



        // 使用jwt
        Map<String, Object> map = loginSuccessJwtHandler(loginInfo);

        return JsonResult.me().setObj(map);
    }


    private Map<String, Object> loginSuccessJwtHandler(LoginInfo loginInfo) {
        try {
            // 创建loginData对象，用于加密
            LoginData loginData = new LoginData();
            loginData.setLoginInfo(loginInfo);

            Map<String, Object> map = new HashMap<>();
            // 判断当前用户类型
            if (loginInfo.getType() == 0){ // 管理员
                // 查找当前用户的权限和菜单
                List<String> permissions = employeeMapper.queryPermissionSnByLoginInfoId(loginInfo.getId());
                List<Menu> menus = employeeMapper.queryMenusByLoginInfoId(loginInfo.getId());
                // 封装进loginData对象中
                loginData.setPermissions(permissions);
                loginData.setMenus(menus);
                // 将当前用户的权限和菜单添加到map，返回给前端
                map.put("permissions", permissions);
                map.put("menus",menus);
            }

            // 获取私钥
            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            // 通过私钥对数据进行加密
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 30*1000);

            //5.装到map返回
            map.put("token",jwtToken);
            map.put("loginInfo",loginInfo);
            return map;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


    @Override
    public JsonResult wechatLogin(String code) {
        // 通过code向微信发送请求获取token和openid
        /*
                {
                    "access_token":"ACCESS_TOKEN",
                    "expires_in":7200,
                    "refresh_token":"REFRESH_TOKEN",
                    "openid":"OPENID",
                    "scope":"SCOPE",
                    "unionid":"UNIONID" "o6_bmasdasdsad6_2sgVt7hMZOPfL"
                }
         */
        String jsonStr = HttpUtil.httpGet(WxConstants.GET_ACK_URL
                .replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code)
        );
        // 将json字符串转为json对象，取出token和openid
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);

        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        // 通过openid去数据库中查询有无该用户，有的话说明已经绑定过，直接登录，没有的话说明第一次绑定
        WxUser dbWxUser = wxUserMapper.queryByOpenId(openid);

        if (dbWxUser != null && dbWxUser.getUser_id()!=null) { // 已经登录过，可以放行
            // 获取登录信息
            LoginInfo loginInfo = loginInfoMapper.queryByUserId(dbWxUser.getUser_id());

            // 生成token存入redis中，用于后面判断登录状态
            /*String token = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(token, loginInfo,30, TimeUnit.MINUTES);

            // 5. 将token与loginInfo对象返回前端
            Map<String, Object> map = new HashMap<>();

            // 清空敏感数据再封装，保证数据安全
            map.put("token", token);
            loginInfo.setSalt(null);
            loginInfo.setPassword(null);
            map.put("loginInfo", loginInfo);*/

            Map<String, Object> map = loginSuccessJwtHandler(loginInfo);

            return JsonResult.me().setObj(map);

        }else {// 首次登录
            // 需要把accessToken和openid封装返回给前端,让前端进行跳转
            Map<String, String> map = new HashMap<>();
            map.put("accessToken",accessToken);
            map.put("openid",openid);
            return JsonResult.me(false).setObj(map);

        }

    }

    @Override
    public Map<String, Object> binder(WxBinderDto wxBinderDto) {
        String phone = wxBinderDto.getPhone();
        String verifyCode = wxBinderDto.getVerifyCode();
        String accessToken = wxBinderDto.getAccessToken();
        String openId = wxBinderDto.getOpenId();

        // 空值校验
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)
        ){
            throw new BusinessException("参数不能为空");
        }
        // 校验验证码是否过期
        Object obj = redisTemplate.opsForValue().get(phone + VerifyCodeConstants.WXBINDER);
        if (obj == null){
            throw new BusinessException("验证码过期，请重新获取");
        }
        // 校验验证码是否正确
        if (!verifyCode.equalsIgnoreCase(obj.toString().split(":")[0])){
            throw new BusinessException("验证码输入错误，请重新输入");
        }

        // 绑定用户
        // 发送第三次请求获取微信用户信息
        String jsonStr = HttpUtil.httpGet(WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId));

        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        // 给微信用户对象赋值
        WxUser wxUser = json2WxUser(jsonObject);

        // 根据手机号码查询user中是否有该手机号码【为了已经手机注册用户使用微信登录】
        User user = userMapper.queryByPhone(phone);
        // 判断是否有该数据
        if (user == null){ // 首次登录注册
            // 根据手机号码去新建一个loginInfo对象
            user = phone2User(phone);
            // 根据user对象去创建一个loginInfo对象
            LoginInfo loginInfo = user2LoginInfo(user);

            // 将数据存入数据库中
            loginInfoMapper.add(loginInfo);

            user.setLogininfo_id(loginInfo.getId());
            userMapper.add(user);

        }  // 不为空，代表之前手机号注册过
        // 将user对象的id 存入wxUser中,并存入数据库
        wxUser.setUser_id(user.getId());
        wxUserMapper.add(wxUser);

        // 获取loginInfo对象
        LoginInfo loginInfo = loginInfoMapper.queryByUserId(user.getId());
        // 将信息返回给前端


       /* Map<String, Object> map = new HashMap<>();
        String token = UUID.randomUUID().toString();

        // 设置三十分钟过期
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
        // 清除隐秘信息
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        map.put("token", token);
        map.put("loginInfo", loginInfo);*/



        Map<String, Object> map = loginSuccessJwtHandler(loginInfo);

        return map;


    }

    /**
     * 根据用户赋值给loginInfo
     * @param user
     * @return
     */
    private LoginInfo user2LoginInfo(User user) {

        LoginInfo loginInfo = new LoginInfo();

        BeanUtils.copyProperties(user, loginInfo);
        loginInfo.setType(1);
        return loginInfo;
    }

    /**
     * 根据手机号给user赋值
     * @param phone
     * @return
     */
    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);

        String salt = StrUtils.getComplexRandomString(32);
        user.setSalt(salt);
        // 密码随机生成一个即可
        String randomPassword = StrUtils.getComplexRandomString(6);
        String password = Md5Utils.encrypByMd5(salt + randomPassword);
        user.setPassword(password);

        return user;

    }

    /**
     * 根据第三次调用微信接口返回参数给wxUser赋值
     * @param jsonObject
     * @return
     */
    private WxUser json2WxUser(JSONObject jsonObject) {
        /*
                {
                    "openid":"OPENID",
                    "nickname":"NICKNAME",
                    "sex":1,
                    "province":"PROVINCE",
                    "city":"CITY",
                    "country":"COUNTRY",
                    "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
                    "privilege":[
                    "PRIVILEGE1",
                    "PRIVILEGE2"
                    ],
                    "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"

                }
         */
        WxUser wxUser = new WxUser();
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(jsonObject.getString("country") + jsonObject.getString("city"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));

        return wxUser;
    }


}
